use byteorder::{ByteOrder, LittleEndian};

use crate::{base_socket_client::BaseSocket consts, errors::ApiCallError, helper};

pub struct HistoryTransactionTick {
    pub time: String,
    pub price: f64,
    pub vol: f64,
    pub buyorsell: i32,
}

// Assuming `TcpStream` is the type you are using for sending data
pub struct GetHistoryTransactionData;

impl GetHistoryTransactionData {
    /// get_history_transaction_data(0, '000001', 0, 10, 20170811)
    ///
    /// market: MARKET_SZ = 0  # 深圳 MARKET_SH = 1  # 上海
    /// code: 600001
    /// start: 1
    /// count: 10
    /// date: 20240123 in the format "YYYYMMDD"
    fn set_params(market: u16, code: &str, start: u16, count: u16, date: u32) -> Vec<u8> {
        // Initialize the package with the hex values
        // pkg = bytearray.fromhex(u'0c 01 30 01 00 01 12 00 12 00 b5 0f')
        let mut buf = vec![
            0x0C, 0x01, 0x30, 0x01, 0x00, 0x01, 0x12, 0x00, 0x12, 0x00, 0xB5, 0x0F, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        ];
        // Pack the date, market, code, start, and count into the package

        // pkg.extend(struct.pack("<IH6sHH", date, market, code, start, count)) 4 2 6 2 2 = 16
        // Use LittleEndian as the byte order
        LittleEndian::write_u32(&mut buf[12..16], date); // Writes 4 bytes for the date
        LittleEndian::write_u16(&mut buf[16..18], market); // Writes 2 bytes for the market
        buf[18..18 + code.as_bytes().len()].copy_from_slice(code.as_bytes()); // Copies the code bytes
        LittleEndian::write_u16(&mut buf[24..26], start); // Writes 2 bytes for the start
        LittleEndian::write_u16(&mut buf[26..28], count); // Writes 2 bytes for the count

        // Update the send package
        buf
    }

    /// market: MARKET_SZ = 0  # 深圳 MARKET_SH = 1  # 上海
    ///
    /// code: e.g. "600001", "000001"
    ///
    /// start: 0
    ///
    /// count: 10
    ///
    /// date: 20240123 in the format "YYYYMMDD"
    pub async fn call_api(
        client: &mut BaseSocket
        market: u16,
        code: &str,
        start: u16,
        count: u16,
        date: u32,
    ) -> Result<Vec<HistoryTransactionTick>, ApiCallError> {
        if market != consts::MARKET_SZ && market != consts::MARKET_SH {
            return Err(ApiCallError::UnknownMarket);
        }
        if count > 2000 {
            return Err(ApiCallError::CountOutOfRange);
        }

        let send_pkg = Self::set_params(market, code, start, count, date);
        let _rs = client.send(&send_pkg).await?;
        let rs = client.recv().await?;

        let prices = Self::parse_response(&rs);
        Ok(prices)
    }

    fn parse_response(body_buf: &[u8]) -> Vec<HistoryTransactionTick> {
        let mut pos = 0;
        let (num,) = (u16::from_le_bytes([body_buf[0], body_buf[1]]),);
        pos += 2;
        let mut ticks = Vec::new();

        // skip 4 bytes
        pos += 4;

        let mut last_price = 0;
        for _ in 0..num {
            // get_time
            // \x80\x03 = 14:56
            let (hour, minute, new_pos) = helper::get_time(body_buf, pos);

            let (price_raw, new_pos) = helper::get_price(body_buf, new_pos);
            let (vol, new_pos) = helper::get_price(body_buf, new_pos);
            let (buyorsell, new_pos) = helper::get_price(body_buf, new_pos);
            let (_, new_pos) = helper::get_price(body_buf, new_pos);

            last_price += price_raw as i32;

            let tick = HistoryTransactionTick {
                time: format!("{:02}:{:02}", hour, minute),
                price: last_price as f64 / 100.0,
                vol: vol as f64,
                buyorsell, // Assuming buyorsell is a string representing a float
            };

            ticks.push(tick);
            pos = new_pos;
        }

        ticks
    }
}

#[cfg(test)]
mod test {
    use std::{thread, time::Duration};

    use crate::{
        base_socket_client::BaseSocket
        get_history_transaction_data::GetHistoryTransactionData, setup_cmd1::SetupCmd1,
        setup_cmd2::SetupCmd2, setup_cmd3::SetupCmd3,
    };

    #[tokio::test]
    async fn test_set_params() {
        // Example usage
        let send_pkg = GetHistoryTransactionData::set_params(4, "600001", 3, 12, 20240123);
        // Now `socket.send_pkg` contains the assembled packet data

        println!("send_pkg={:?}", hex::encode(&send_pkg));

        assert_eq!(
            hex::encode(&send_pkg),
            "0c013001000112001200b50ffbd63401040036303030303103000c00"
        );

        // >>> market=4
        // >>> code='600001'.encode("utf-8")
        // >>> start=3
        // >>> count=12
        // >>> date=20240123
        // >>> pkg = bytearray.fromhex(u'0c 01 30 01 00 01 12 00 12 00 b5 0f')
        // >>> pkg = pkg.extend(struct.pack("<IH6sHH", date, market, code, start, count))
        // >>> print("Package in hex:", " ".join(format(b, "02x") for b in pkg))
        // Package in hex: 0c 01 30 01 00 01 12 00 12 00 b5 0f fb d6 34 01 04 00 36 30 30 30 30 31 03 00 0c 00
        //                 0c 01 30 01 00 01 12 00 12 00 b5 0f fb d6 34 01 04 00 36 30 30 30 30 31 03 00 0c 00
    }

    #[tokio::test]
    async fn test_parse_response() {
        let decompressed_body= "64 00 cd cc 14 41 79 03 a4 0e 2f 00 00 79 03 00 85 02 00 00 7a 03 00 1e 00 00 7a 03 00 96 01 00 00 7a 03 41 1c 01 00 7a 03 00 01 01 00 7a 03 00 31 01 00 7a 03 00 1a 01 00 7a 03 00 0b 01 00 7a 03 01 af 01 00 00 7a 03 41 0b 01 00 7a 03 01 32 00 00 7a 03 00 92 02 00 00 7a 03 41 18 01 00 7b 03 01 01 00 00 7b 03 00 9c 01 00 00 7b 03 41 01 01 00 7b 03 00 8f 02 01 00 7b 03 00 8f 01 01 00 7b 03 00 02 01 00 7b 03 00 04 01 00 7b 03 00 b5 07 01 00 7b 03 01 96 01 00 00 7b 03 00 01 00 00 7b 03 41 32 01 00 7b 03 01 90 02 00 00 7b 03 00 0a 00 00 7c 03 41 8b 06 01 00 7c 03 00 97 01 01 00 7c 03 00 0a 01 00 7c 03 00 1d 01 00 7c 03 01 05 00 00 7c 03 41 03 01 00 7c 03 00 0a 01 00 7c 03 00 11 01 00 7c 03 00 29 01 00 7c 03 01 2a 00 00 7c 03 41 04 01 00 7c 03 01 32 00 00 7c 03 41 90 01 01 00 7c 03 01 87 02 00 00 7c 03 41 0f 01 00 7d 03 00 96 01 01 00 7d 03 00 23 01 00 7d 03 01 0b 00 00 7d 03 41 be 02 01 00 7d 03 00 9a 01 01 00 7d 03 01 06 00 00 7d 03 41 ac 04 01 00 7d 03 00 20 01 00 7d 03 00 2d 01 00 7d 03 00 0a 01 00 7d 03 00 85 04 01 00 7d 03 01 90 02 00 00 7e 03 41 0e 01 00 7e 03 00 a9 01 01 00 7e 03 00 a7 01 01 00 7e 03 01 3c 00 00 7e 03 41 0a 01 00 7e 03 01 19 00 00 7e 03 41 06 01 00 7e 03 00 08 01 00 7e 03 00 14 01 00 7e 03 00 03 01 00 7e 03 01 a4 01 00 00 7e 03 00 89 02 00 00 7e 03 41 07 01 00 7f 03 00 86 03 01 00 7f 03 00 05 01 00 7f 03 00 ac 03 01 00 7f 03 01 14 00 00 7f 03 00 18 00 00 7f 03 41 07 01 00 7f 03 01 14 00 00 7f 03 41 25 01 00 7f 03 00 22 01 00 7f 03 00 14 01 00 7f 03 01 09 00 00 7f 03 41 39 01 00 7f 03 00 0b 01 00 7f 03 01 a2 03 00 00 7f 03 41 19 01 00 7f 03 00 37 01 00 80 03 00 15 01 00 80 03 01 1d 00 00 80 03 00 32 00 00 80 03 00 8f 03 00 00 80 03 41 ae 01 01 00 80 03 00 96 01 01 00 80 03 01 10 00 00 80 03 00 21 00 00 80 03 00 1e 00 00 80 03 00 25 00 00 80 03 00 0a 00 00 80 03 41 05 01 00 80 03 00 17 01 00 80 03 00 06 01 00 80 03 00 09 01 00 81 03 00 09 01 00 83 03 00 8b 18 01 00";

        let buf = hex::decode(decompressed_body.replace(" ", "")).unwrap();
        let prices = GetHistoryTransactionData::parse_response(&buf);

        assert_eq!(prices.len(), 100);

        println!("#    time  price   vol  buyorsell");
        for (i, p) in prices.iter().enumerate() {
            println!("[{i}] {} {} {} {}", p.time, p.price, p.vol, p.buyorsell);
        }
        assert_eq!(prices.get(0).unwrap().time, "14:49");
        assert_eq!(prices.get(0).unwrap().price, 9.32);
        assert_eq!(prices.get(0).unwrap().vol, 47f64);
        assert_eq!(prices.get(0).unwrap().buyorsell, 0);

        assert_eq!(prices.get(2).unwrap().time, "14:50");
        assert_eq!(prices.get(2).unwrap().price, 9.32);
        assert_eq!(prices.get(2).unwrap().vol, 30f64);
        assert_eq!(prices.get(2).unwrap().buyorsell, 0);

        assert_eq!(prices.get(99).unwrap().time, "14:59");
        assert_eq!(prices.get(99).unwrap().price, 9.31);
        assert_eq!(prices.get(99).unwrap().vol, 1547f64);
        assert_eq!(prices.get(99).unwrap().buyorsell, 1);
    }

    #[tokio::test]
    async fn test_call_api() {
        let mut client = BaseSocketClient::new(false, false, false, false);
        match client.connect(Some("119.147.212.81"), Some(7709)).await {
            Ok(_) => {
                println!("[119.147.212.81] Connected!");

                let _ = SetupCmd1::new().call_api(&mut client).await.unwrap();
                let _ = SetupCmd2::new().call_api(&mut client).await.unwrap();
                let _ = SetupCmd3::new().call_api(&mut client).await.unwrap();

                let start = 0;
                let count = 20;
                let mut prices_000001 = GetHistoryTransactionData::call_api(
                    &mut 
                    0,
                    "000001",
                    start,
                    count,
                    20170209,
                )
                .await
                .unwrap();

                let mut prices_513130 = GetHistoryTransactionData::call_api(
                    &mut 
                    1,        // 0,
                    "513130", // "000001",
                    start,
                    count,
                    20240319, // 20170209,
                )
                .await
                .unwrap();

                //
                prices_000001.reverse();
                prices_513130.reverse();

                println!("\n000001 time price vol buyorsell");
                for (i, p) in prices_000001.iter().enumerate() {
                    println!(
                        "[{:00005}] {} {} {} {}",
                        i + start as usize,
                        p.time,
                        p.price,
                        p.vol,
                        p.buyorsell
                    );
                }

                println!("\n513130 time price vol buyorsell");
                for (i, p) in prices_513130.iter().enumerate() {
                    println!(
                        "[{:00005}] {} {} {} {}",
                        i + start as usize,
                        p.time,
                        p.price,
                        p.vol,
                        p.buyorsell
                    );
                }
            }
            Err(e) => println!("Connection error: {:?}", e),
        }

        let _rs = client.disconnect().await.unwrap();
    }
}
